﻿package com.zhonglian.elisa.view;

import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.util.Size;

import com.zhonglian.elisa.utils.LogUtil;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * Created : Yx
 * Time : 2020/4/24 17:19
 * Describe :https://www.jianshu.com/p/e94401a0597f
...https://www.sogou.com/link?url=hedJjaC291OV7dVab-QfvHtdr0qpeLU_AqIhqkM7zQ36Bd5IxURT1g.. */
public class CameraUtils {
    private static final long PREVIEW_SIZE_MIN = 720 * 480;

    /**
     * 1.获取相机的输出尺寸列表，降序排序
     *
     * @param mCameraManager 相机管理器
     * @param cameraId       相机id
     * @return
     */
    public static List<Size> getCameraOutputSizes(CameraManager mCameraManager, String cameraId) {
        try {
            CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(cameraId);
            StreamConfigurationMap configs = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
            /*
             * StreamConfigurationMap.getOutputSizes() 方法获取尺寸列表，该方法会要求你传递一个 Class 类型，然后根据这个类型返回对应的尺寸列表，如果给定的类型不支持，则返回 null，
             * 你可以通过 StreamConfigurationMap.isOutputSupportedFor() 方法判断某一个类型是否被支持，常见的类型有：
             *
             * ImageReader：常用来拍照或接收 YUV 数据。
             * MediaRecorder：常用来录制视频。
             * MediaCodec：常用来录制视频。
             * SurfaceHolder：常用来显示预览画面。
             * SurfaceTexture：常用来显示预览画面。
             * 预览尺寸都打印出来看时，会发现 ，就是预览尺寸的宽是长边，高是短边,是因为相机输出尺寸默认是横向的（宽>高）宽是长边，而高是短边。
             */
            List<Size> sizes = Arrays.asList(configs.getOutputSizes(SurfaceTexture.class));//SurfaceTexture.class输出类
            Collections.sort(sizes, new Comparator<Size>() {//(返回一个int值，如果0说明o1和o2相等，如果返回负值，那么o1和o2会倒序排序，返回正值，那么o1和o2会正序排序。)
                @Override
                public int compare(Size o1, Size o2) {
                    return o1.getWidth() * o1.getHeight() - o2.getWidth() * o2.getHeight();
                }
            });
            Collections.reverse(sizes);
            return sizes;
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 1v1根据输出格式获取指定相机的输出尺寸列表
     *
     * @param mCameraManager 相机管理器
     * @param cameraId       相机id
     * @param format         输出格式
     * @return
     */
    public List<Size> getCameraOutputSizes(CameraManager mCameraManager, String cameraId, int format) {
        try {
            CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(cameraId);
            StreamConfigurationMap configs = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
            return Arrays.asList(configs.getOutputSizes(format));
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }

        return null;
    }


    /**
     * 2.从尺寸列表中 ，获得最佳尺寸
     * 预览模式下的最佳尺寸(手机屏幕开始都是默认横向，所以竖起来以后高便是宽)
     * 由于全屏展示，所以先获得屏幕尺寸，用屏幕尺寸的高 比 宽 （横则反） 与尺寸列表的 宽比高 比较
     *
     * @param screenWidth  屏幕宽
     * @param screenHeight 屏幕高
     * @param outputSizes  相机尺寸集合
     */
    public static Size updateCameraPreviewWithVideoMode(int screenWidth, int screenHeight, List<Size> outputSizes) {
        List<Size> sizes = new ArrayList<>();
        Size previewSize = new Size(screenHeight, screenWidth);
        //计算预览窗口高宽比，高宽比，高宽比
        float ratio = ((float) screenHeight / screenWidth);//高除宽
        //首先选取宽高比与预览窗口高宽比一致且最大的输出尺寸
        for (int i = 0; i < outputSizes.size(); i++) {
            if (((float) outputSizes.get(i).getWidth()) / outputSizes.get(i).getHeight() == ratio) {
                sizes.add(outputSizes.get(i));
            }
        }
        if (sizes.size() > 0) {
            previewSize = Collections.max(sizes, new CompareSizesByArea());
            LogUtil.iYx("Size 1:" + previewSize.getWidth() + "/" + previewSize.getHeight());
            return previewSize;
        }
        //如果不存在宽高比与预览窗口高宽比一致的输出尺寸，则选择与其宽高比最接近的输出尺寸
        sizes.clear();
        float detRatioMin = Float.MAX_VALUE;
        for (int i = 0; i < outputSizes.size(); i++) {
            Size size = outputSizes.get(i);
            float curRatio = ((float) size.getWidth()) / size.getHeight();
            if (Math.abs(curRatio - ratio) < detRatioMin) {
                detRatioMin = curRatio;
                previewSize = size;
            }
        }
        if (previewSize.getWidth() * previewSize.getHeight() > PREVIEW_SIZE_MIN) {
            //previewSize 不变
        }
        LogUtil.iYx("Size 2:" + previewSize.getWidth() + "/" + previewSize.getHeight());
        //如果宽高比最接近的输出尺寸太小，则选择与预览窗口面积最接近的输出尺寸
        long area = screenHeight * screenWidth;
        long detAreaMin = Long.MAX_VALUE;
        for (int i = 0; i < outputSizes.size(); i++) {
            Size size = outputSizes.get(i);
            long curArea = size.getWidth() * size.getHeight();
            if (Math.abs(curArea - area) < detAreaMin) {
                detAreaMin = curArea;
                previewSize = size;
            }
        }
        LogUtil.iYx("Size 3:" + previewSize.getWidth() + "/" + previewSize.getHeight());
        return previewSize;
    }

    /**
     * 统一方法调用
     *
     * @return
     */
    public static Size getSize(CameraManager mCameraManager, String mCameraID, int screenWidth, int screenHeight) {
        //最佳尺寸
        List<Size> sizeList = CameraUtils.getCameraOutputSizes(mCameraManager, mCameraID);
        return CameraUtils.updateCameraPreviewWithVideoMode(screenWidth, screenHeight, sizeList);
    }


    public static class CompareSizesByArea implements Comparator<Size> {
        @Override
        public int compare(Size lhs, Size rhs) {//(返回一个int值，如果0说明o1和o2相等，如果返回负值，那么o1和o2会倒序排序，返回正值，那么o1和o2会正序排序。)
            return Long.signum((long) lhs.getWidth() * lhs.getHeight() -
                    (long) rhs.getWidth() * rhs.getHeight());
        }
    }
}
